package com.geneqiao.jdbc;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.log4j.Logger;

import com.geneqiao.jdbc.jpa.TableStruct;
import com.geneqiao.jdbc.pool.JDBCDataSource;
import com.geneqiao.jdbc.pool.JDBCMap;
import com.geneqiao.jdbc.util.DataUtils;
import com.geneqiao.jdbc.util.ObjectUtils;
import com.geneqiao.jdbc.util.SQLType;

public class FastSession extends AbstractSession implements ISession
{

	// 日志
	private static final Logger logger = Logger.getLogger(FastSession.class);

	private FastSession(JDBCDataSource dataSource)
	{
		this.jdbcDataSource = dataSource;
	}

	public <T> List<T> buildQuery(Class<T> t, String sql, Connection conn, Object... objects) throws SQLException
	{
		long start = getNow();
		List<T> vos = new ArrayList<>();
		ResultSet rs = ObjectUtils.buildResultSet(conn, sql, objects);
		Map<Integer, String> map;
		long start2 = getNow();
		String cacheKey = t.getName() + "_" + sql;
		if (JDBCMap.containsKey(cacheKey))
		{
			map = JDBCMap.get(cacheKey);
			if (logger.isDebugEnabled())
			{
				logger.debug("Load map from cache => " + (getNow() - start2) + "ns");
			}
		}
		else
		{
			map = DataUtils.buildColumnMap(t, rs.getMetaData());
			JDBCMap.put(cacheKey, map);
			if (logger.isDebugEnabled())
			{
				logger.debug("Build map => " + (getNow() - start2) + "ns");
			}
		}
		if (map.size() > 0)
		{
			while (rs.next())
			{
				vos.add(DataUtils.copyValue(map, rs, t));
			}
		}
		rs.getStatement().close();
		rs.close();
		if (logger.isDebugEnabled())
		{
			logger.debug("SQL => " + sql);
			logger.debug("Result rows => " + (vos != null ? vos.size() : 0) + "; columns => " + map.size()
					+ "; time => " + (getNow() - start) + "ns");
		}
		return vos;
	}

	@SuppressWarnings("unchecked")
	@Override
	public <T> List<T> buildQueryList(String sql, Connection conn, Object... objects) throws SQLException
	{
		long start = getNow();
		ResultSet rs = ObjectUtils.buildResultSet(conn, sql, objects);
		List<T> dataList = new ArrayList<>();
		while (rs.next())
		{
			dataList.add((T) rs.getObject(1));
		}
		rs.getStatement().close();
		rs.close();
		if (logger.isDebugEnabled())
		{
			logger.debug("SQL => " + sql);
			logger.debug("Times => " + (getNow() - start) + "ns");
		}
		return dataList;
	}

	@Override
	public Map<String, Object> buildQueryMap(String sql, Connection conn, Object... objects) throws SQLException
	{
		long start = getNow();
		ResultSet rs = ObjectUtils.buildResultSet(conn, sql, objects);
		if (rs.getMetaData().getColumnCount() != 2)
		{
			logger.error("字典查询只能包含两列字段");
			return null;
		}
		Map<String, Object> map = new HashMap<>();
		while (rs.next())
		{
			map.put(rs.getString(1), rs.getObject(2));
		}
		rs.getStatement().close();
		rs.close();
		if (logger.isDebugEnabled())
		{
			logger.debug("SQL => " + sql);
			logger.debug("Map<K,V> => " + map.size() + "; Times => " + (getNow() - start) + "ns");
		}
		return map;
	}

	@Override
	public Object buildGetSingle(String sql, Connection conn, Object... objects) throws SQLException
	{
		long start = getNow();
		Object obj = null;
		ResultSet rs = ObjectUtils.buildResultSet(conn, sql, objects);
		if (rs.next())
			obj = rs.getObject(1);
		rs.getStatement().close();
		rs.close();
		if (logger.isDebugEnabled())
		{
			logger.debug("SQL => " + sql);
			logger.debug("Result => " + obj + "; Times => " + (getNow() - start) + "ns");
		}
		return obj;
	}

	@Override
	public boolean buildExecute(String sql, Connection conn, Object... objects) throws SQLException
	{
		long start = getNow();
		Integer num = ObjectUtils.executeUpdate(conn, sql, objects);
		if (logger.isDebugEnabled())
		{
			logger.debug("SQL => " + sql);
			logger.debug("Rows => " + num + ", Times => " + (getNow() - start) + "ns");
		}
		return true;
	}

	@Override
	public boolean execute(List<Sql> sqlList, boolean withTran)
	{
		long start = getNow();
		long start2 = 0L;
		Connection conn = null;
		try
		{
			conn = jdbcDataSource.getConnection();
			if (withTran)
				conn.setAutoCommit(false);
			Integer num = 0;
			for (Sql sql : sqlList)
			{
				start2 = getNow();
				num = ObjectUtils.executeUpdate(conn, sql.getSqlString(), sql.getParameters());
				if (logger.isDebugEnabled())
				{
					logger.debug("SQL => " + sql.getSqlString());
					logger.debug("Rows => " + num + ", Times => " + (getNow() - start2) + "ns");
				}
			}
			if (withTran)
			{
				conn.commit();
				logger.debug("committed");
			}
			if (logger.isDebugEnabled())
			{
				logger.debug("Executed, Nums => " + sqlList.size() + ", Times => " + (getNow() - start) + "ns");
			}
			return true;
		}
		catch (SQLException e)
		{
			if (withTran)
				JDBCDataSource.rollback(conn);
			logger.error("Execute sqls faild", e);
			return false;
		}
		finally
		{
			if (withTran)
				jdbcDataSource.setAutoCommit(conn);
			jdbcDataSource.releaseConnection(conn);
		}
	}

	@Override
	public boolean save(List<Object> objs)
	{
		long start = getNow();
		long start2 = 0L;
		Connection conn = null;
		try
		{
			conn = jdbcDataSource.getConnection();
			TableStruct map;
			PreparedStatement ps;
			Object result = null;
			ResultSet rs;
			int num = 0;
			for (Object obj : objs)
			{
				start2 = getNow();
				map = DataUtils.buildSQLMap(obj, SQLType.INSERT);
				if (map == null)
				{
					logger.error("获取对象" + obj + "的注解失败");
					continue;
				}
				ps = ObjectUtils.buildStatement(conn, map.getBuildMap(), map.hasGenerageKey());
				if (ps.executeUpdate() > 0 && map.hasGenerageKey())
				{
					rs = ps.getGeneratedKeys();
					if (rs.next())
						result = rs.getInt(1);
					DataUtils.setValue(obj, map.getGenerateKey(), result);
					rs.close();
				}
				ps.close();
				num++;
				if (logger.isDebugEnabled())
				{
					logger.debug("Saved, ID => " + result + ", Times => " + (getNow() - start2) + "ns");
				}
			}
			if (logger.isDebugEnabled())
			{
				logger.debug("Finish, total => " + objs.size() + ", saved => " + num + ", Times => "
						+ (getNow() - start) + "ns");
			}
			return true;
		}
		catch (SQLException e)
		{
			logger.error("Save object faild", e);
			return false;
		}
		finally
		{
			jdbcDataSource.releaseConnection(conn);
		}
	}

	/**
	 * 更新对象
	 * 
	 * @param obj
	 * @param notNull
	 *            是否只更新不为NULL字段
	 * @return
	 */
	@Override
	public boolean update(Object obj, boolean notNull)
	{
		long start = getNow();
		Connection conn = null;
		try
		{
			TableStruct map = DataUtils.buildSQLMap(obj, (notNull ? SQLType.UPDATENOTNULL : SQLType.UPDATE));
			if (map == null || !map.hasPrimaryKey())
				return false;
			conn = jdbcDataSource.getConnection();
			PreparedStatement ps = ObjectUtils.buildStatement(conn, map.getBuildMap());
			Integer num = ps.executeUpdate();
			ps.close();
			if (logger.isDebugEnabled())
			{
				logger.debug("Updated, Rows => " + num + ", Times => " + (getNow() - start) + "ns");
			}
			return true;
		}
		catch (SQLException e)
		{
			logger.error("Update object faild", e);
			return false;
		}
		finally
		{
			jdbcDataSource.releaseConnection(conn);
		}
	}

	public static FastSession getInstence(JDBCDataSource dataSource)
	{
		return new FastSession(dataSource);
	}
}
